/**
 * @file timer_operation.\
  76r* @author your name (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2025-04-30
 *
 * @copyright Copyright (c) 2025
 *
 */
#include "timer_operation.h"
#include "xmzTypeDefine.h"

/**
 * @brief 接通延时定时器（TON），适用于嵌入式环境
 *
 * 该函数实现接通延时定时器（Timer On-Delay），在启动输入 IN
 * 的信号上升沿（从“0”变为“1”）时启动。
 * 定时器启动后，记录启动时间（start_time），使用时间差（ET -
 * start_time）计算当前时间值 ET。 当 ET >= PT 时，置位输出 Q 为“1”；当 IN = 0
 * 时，Q 和 ET 复位为“0”。 如果检测到新的 IN 上升沿，定时器重新启动，ET 重置为
 * 0。 ET 由定时中断提供（system_time），需单调递增，单位为毫秒。 如果 PT
 * 不是正数，Q 和 ET 复位为 0，返回 false。 注意：Time 为 int32_t，system_time
 * 在约24.85天后溢出，可能导致 ET 计算错误，后期可优化为 uint32_t 或 int64_t。
 *
 * @param IN 启动输入（true 表示“1”，false 表示“0”）
 * @param PT 接通延时的持续时间（毫秒，必须为正数）
 * @param ET 当前时间值（毫秒，由定时中断提供，需单调递增）
 * @param instance 指向 IEC_Timer 结构体的指针，存储定时器实例数据
 * @return Bool 返回 instance->Q（输出状态）
 */
Bool TON(Bool IN, Time PT, Time ET, IEC_Timer *instance) {
  // 更新实例的 IN 和 PT
  instance->IN = IN;
  instance->PT = PT;

  // 确保 PT 为正数
  if (PT <= 0) {
    instance->Q = false;
    instance->ET = 0;
    instance->is_running = false;
    instance->last_in = IN;
    return false;
  }

  // 检测 IN 的上升沿：上一次 IN 为 0，当前 IN 为 1
  Bool rising_edge = !instance->last_in && IN;

  if (rising_edge) {
    // 上升沿触发，启动定时器，记录启动时间
    instance->start_time = ET;
    instance->is_running = true;
    instance->ET = 0;
  }

  if (IN && instance->is_running) {
    // IN 为 1，计算时间差，更新 ET
    instance->ET = ET - instance->start_time;
    if (instance->ET >= PT) {
      instance->Q = true; // 达到或超过 PT，置位 Q
      instance->ET = PT;  // ET 停止在 PT
    } else {
      instance->Q = false; // 未达到 PT，Q 保持 0
    }
  } else {
    // IN 为 0 或定时器未运行，复位 Q 和 ET
    instance->Q = false;
    instance->ET = 0;
    instance->is_running = false;
  }

  // 更新 last_in 为当前 IN 状态
  instance->last_in = IN;

  return instance->Q;
}

/**
 * @brief 启动接通延时定时器（CoilTON），适用于嵌入式环境
 *
 * 该函数实现“启动接通延时定时器”指令，在输入 in
 * 的信号上升沿（从“0”变为“1”）时启动 IEC 定时器。
 * 定时器运行指定的接通延时时间（value）。当 in = 1 且 ET >= value 时，输出 Q
 * 置位为“1”。 若 in 在计时结束前变为“0”，复位定时器，Q = 0，ET =
 * 0。新上升沿重新启动定时器。 定时器状态保存在 operand->Q
 * 中，可通过常开/常闭触点查询。 使用 system_time（由定时中断累加）计算相对时间
 * ET = system_time - start_time。 如果 value 不是正数，Q 和 ET 复位为 0，返回
 * false。 注意：Time 为 int32_t，system_time 在约24.85天后溢出，可能导致 ET
 * 计算错误，后期可优化为 uint32_t 或 int64_t。
 *
 * @param in RLO 输入（true 表示“1”，false 表示“0”）
 * @param value 接通延时的持续时间（毫秒，必须为正数，<操作数 1>）
 * @param system_time 当前系统时间（毫秒，由定时中断提供，需单调递增）
 * @param operand 指向 IEC_Timer 结构体的指针，存储定时器实例数据（<操作数 2>）
 * @return Bool 返回 operand->Q（输出状态）
 */
Bool CoilTON(Bool in, Time value, Time system_time, IEC_Timer *operand) {
  // 更新实例的 IN 和 PT
  operand->IN = in;
  operand->PT = value;

  // 确保 value 为正数
  if (value <= 0) {
    operand->Q = false;
    operand->ET = 0;
    operand->is_running = false;
    operand->last_in = in;
    return false;
  }

  // 检测 in 的上升沿
  Bool rising_edge = !operand->last_in && in;

  if (rising_edge) {
    // 上升沿触发，启动定时器，记录启动时间
    operand->start_time = system_time;
    operand->is_running = true;
    operand->ET = 0;
  }

  if (in && operand->is_running) {
    // in 为 1，定时器运行，计算时间差
    operand->ET = system_time - operand->start_time;
    if (operand->ET >= value) {
      operand->Q = true;   // 计时结束，Q 置位
      operand->ET = value; // ET 停止在 value
    } else {
      operand->Q = false; // 未到时间，Q 保持 0
    }
  } else {
    // in 为 0 或定时器未运行，复位定时器
    operand->Q = false;
    operand->ET = 0;
    operand->is_running = false;
  }

  // 更新 last_in
  operand->last_in = in;

  return operand->Q;
}

Bool ResetIECTimerCoil(Bool in, IEC_Timer *operand) {
  if (!in)
    return false;
  operand->IN = false;
  operand->PT = 0;
  operand->Q = false;
  operand->ET = 0;
  operand->is_running = false;
  operand->last_in = false;
  operand->start_time = 0;
  return true;
}
